/* 
 * File:   SwitchCurrentStackCommand.cpp
 * Author: Gab'z
 * 
 * Created on 5 juin 2012, 02:38
 */

#include "SwitchCurrentStackCommand.h"
#include "../../Context.h"
#include "../../../model/command/literal/StackList.h"
#include "../../memento/SwitchCurrentStackMemento.h"
#include "../../../exception/MementoException.h"
#include "../literal/Constant.h"

namespace calculator {

    SwitchCurrentStackCommand::SwitchCurrentStackCommand(const std::string name) : StackListCommand(name) {
    }

    SwitchCurrentStackCommand::SwitchCurrentStackCommand(const SwitchCurrentStackCommand& orig) : StackListCommand(orig._name) {
    }

    SwitchCurrentStackCommand::~SwitchCurrentStackCommand() {
    }

    SwitchCurrentStackCommand* SwitchCurrentStackCommand::clone() const {
        return new SwitchCurrentStackCommand(_name);
    }

    std::string SwitchCurrentStackCommand::isExecutable() const {
        // le premier nombre de la pile doit être un entier,
        if (Context::getInstance()->getCurrentStack()->size() == 0) return "Empty stack";
        const Integer* integer = dynamic_cast<const Integer*> (Context::getInstance()->getCurrentStack()->top());
        // ce nombre doit designer une pile déja créée
        if (integer == 0 || integer->value() < 0) return "Stack top must be a positive Integer";
        if ((unsigned int) integer->value() >= Context::getInstance()->getStackList()->size()) return "Out of stackList";
        return "";
    }

    const Memento* SwitchCurrentStackCommand::createMemento() const throw (CommandException) {
        if (Context::getInstance()->getCurrentStack()->size() == 0) throw CommandException("Empty stack");
        const Integer* integer = dynamic_cast<const Integer*> (Context::getInstance()->getCurrentStack()->top());

        return new SwitchCurrentStackMemento(clone(), integer->clone(), (unsigned int) Context::getInstance()->getStackList()->getCurrentStackIndex());
    }

    void SwitchCurrentStackCommand::restoreFromMemento(const Memento* memento) const throw (MementoException) {
        const SwitchCurrentStackMemento* switchCurrentStackMemento = dynamic_cast<const SwitchCurrentStackMemento*> (memento);
        if (switchCurrentStackMemento == 0) {
            std::string str = "Bad Memento type, expected : SwitchCurrentStackMemento, was ";
            str += typeid (*memento).name();
            throw MementoException(str);
        }
        Context::getInstance()->getStackList()->setCurrentStack(switchCurrentStackMemento->_currentMementoIndex);
        Context::getInstance()->getCurrentStack()->push(switchCurrentStackMemento->_integer);
    }

    void SwitchCurrentStackCommand::apply(StackList* stacklist) const throw (CommandException) {
        if (stacklist->getCurrentStack()->size() == 0) throw CommandException("Empty stacklist");
        const Integer* integer = dynamic_cast<const Integer*> (stacklist->getCurrentStack()->top());
        if (integer == 0 || integer->value() < 0) throw CommandException("Stack top must be a positive Integer");
        unsigned int targetStackIndex = (unsigned int) integer->value();

        if (targetStackIndex >= stacklist->size()) throw CommandException("Out of stacklist");
        // on retire l'entier utilisé avant de changer de pile
        const Constant* topConstant = stacklist->getCurrentStack()->top()->clone();
        stacklist->getCurrentStack()->pop();
        try {
            stacklist->setCurrentStack(targetStackIndex);
        } catch (ContextException& e) {
            stacklist->getCurrentStack()->push(topConstant);
            throw CommandException(e.what());
        }
        delete topConstant;
    }
}
